﻿
#if defined(_MSC_VER) /*&& defined(_WIN32) && defined(WIN32)*/
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>

#pragma warning(disable:4996) // #define _WINSOCK_DEPRECATED_NO_WARNINGS // The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name: _stricmp.
#include <WinSock2.h>
#include <Ws2tcpip.h>

#pragma comment(lib, "ws2_32")
#elif defined(unix) || defined(__unix) || defined(__unix__)
//#warning You are using UNIX
#elif defined(__APPLE__) || defined(__MACH__)
//#warning You are using Mac OS X
#elif defined(__linux__)
//#warning You are using Linux
#elif defined(__FreeBSD__)
//#warning You are using FreeBSD
#else
#error Unknown OS
#endif

#include <stdio.h>

#include <vector>

/// 用 select 模型建立的 Server

const unsigned short SERVER_PORT = 4444;

std::vector<SOCKET> g_clients;

// -1 客户端退出, -2 发生异常
int process(SOCKET _cSock)
{
    int nret = 0;
    char _recvBuff[128];

    do
    {
        nret = recv(_cSock, _recvBuff, sizeof(_recvBuff), 0);
        if (SOCKET_ERROR == nret) {
            printf("recv(_cSock ) 失败.\n");
            nret = -2;
            break;
        }
        else if (0 == nret) {
            printf("客户端 socket 已关闭(gracefully closed).\n");
            //closesocket(_cSock);
            //_cSock = INVALID_SOCKET;
            nret = -1;
            continue;
        }

        printf("接收到的命令：[ %s ].\n", _recvBuff);

        if (0 == stricmp(_recvBuff, "exit")) {
            printf("客户端已退出.\n");
            //closesocket(_cSock);
            //_cSock = INVALID_SOCKET;
            nret = -1;
            continue;
        }
        else if (0 == stricmp(_recvBuff, "getName")) {
            char msgBuf[] = "小强哦";
            nret = send(_cSock, msgBuf, sizeof(msgBuf), 0);
        }
        else {
            char msgBuf[] = "???.我是服务器.";
            nret = send(_cSock, msgBuf, sizeof(msgBuf), 0);
        }

        nret = 0;
    } while (false);

    return nret;
}

int main(int argc, const char* argv[])
{
#if defined(_WIN32) && defined(WIN32) && defined(_MSC_VER)
    WORD wVer = MAKEWORD(2, 2);
    WSADATA data;
    WSAStartup(wVer, &data);
#endif

    SOCKET _sock = INVALID_SOCKET;
    
    int nret = 0;

    do
    {
        _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if (INVALID_SOCKET == _sock) {
            printf("socket函数调用失败\n");
            break;
        }

        sockaddr_in _sin;
        _sin.sin_family = AF_INET;
        _sin.sin_port = htons(SERVER_PORT);
        _sin.sin_addr.S_un.S_addr = inet_addr("0.0.0.0");//INADDR_ANY

        nret = bind(_sock, (sockaddr*)&_sin, sizeof(sockaddr_in));
        if (SOCKET_ERROR == nret) {
            printf("bind 失败\n");
            break;
        }

        nret = listen(_sock, 5); //SOMAXCONN,  SOMAXCONN_HINT(  number )
        if (SOCKET_ERROR == nret) {
            printf("listen 失败\n");
            break;
        }

        sockaddr_in clientSocket;
        int addrLen = 0;
        
        while (TRUE)
        {
            SOCKET _cSock = INVALID_SOCKET;
            fd_set fdRead;
            fd_set fdWrite;
            fd_set fdException;
            struct timeval tv = { 0, 55 };

            FD_ZERO(&fdRead);
            FD_ZERO(&fdWrite);
            FD_ZERO(&fdException);

            FD_SET(_sock, &fdRead);
            FD_SET(_sock, &fdWrite);
            FD_SET(_sock, &fdException);

            for (int i = (int)g_clients.size() - 1; i >= 0; i--) {
                FD_SET(g_clients[i], &fdRead);
            }

            nret = select(0, &fdRead, &fdWrite, &fdException, &tv);
            if (SOCKET_ERROR == nret) {
                printf("select 失败.\n");
                break;
            }
            if (FD_ISSET(_sock, &fdRead)) {

                FD_CLR(_sock, &fdRead);

                addrLen = (int)sizeof(sockaddr_in);
                _cSock = accept(_sock, (sockaddr*)&clientSocket, &addrLen);
                if (INVALID_SOCKET == _cSock) {
                    printf("accept 失败\n");
                    break;
                }
                // inet_ntoa 这个函数应该是使用了静态变量
                char* ipClient = inet_ntoa(clientSocket.sin_addr);
                unsigned short cPort = ntohs(clientSocket.sin_port);
                printf("已接受客户端( %s:%d )的连接. socket=%d.\n", ipClient, cPort, (int)_cSock);

                g_clients.push_back(_cSock);
            }

            
            for (int i = 0; i < (int)fdRead.fd_count; i++) 
            {
                nret = process(fdRead.fd_array[i]);
                if (nret < 0) {
                    auto iter = find(g_clients.begin(), g_clients.end(), fdRead.fd_array[i]);
                    if (iter != g_clients.end()) {
                        closesocket(*iter);
                        g_clients.erase(iter);
                    }
                        
                }
            }
            
            
        }


    } while (0);

    for (int i = 0; i < (int)g_clients.size(); i++)
    {
        closesocket(g_clients[i]);
    }

    if (INVALID_SOCKET != _sock)
        closesocket(_sock);

#if defined(_WIN32) && defined(WIN32) && defined(_MSC_VER)
    WSACleanup();
#endif
    printf("程序退出...\n");
    getchar();
    return 0;
}